package de.mmis.core.infrastructure.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;

import javax.swing.*;
import javax.swing.table.TableRowSorter;

import de.mmis.core.base.abstracttree.TreeParserException;
import de.mmis.core.base.event.Observable;
import de.mmis.core.base.gui.AbstractControlComponent;
import de.mmis.core.base.infrastructure.Infrastructure;
import de.mmis.core.base.infrastructure.InfrastructureException;
import de.mmis.core.base.infrastructure.PublishedDeviceDescription;
import de.mmis.core.infrastructure.DistributedInfrastructure;
import de.mmis.core.sexpression.SExpressionParser;

public class MonitorControl extends AbstractControlComponent<Infrastructure>
		implements Scrollable {

	public MonitorControl() {
		super(100, Infrastructure.class, "Monitor");
	}

	private static final long serialVersionUID = 3864676638553634916L;

	private class DeviceList extends JPanel {

		private static final long serialVersionUID = 1066393489926496452L;

		private JTextField text_filter;
		private JButton button_refresh;

		private JTextField text_infos;

		private JTable table_devices;
		private TableRowSorter<DeviceTableModel> tableSorter;
		private JScrollPane scroll_devices;

		LinkedList<Object> keys;

		public DeviceList() {
			initGUI();
		}

		private void initGUI() {
			GridBagLayout layout = new GridBagLayout();
			layout.rowWeights = new double[] { 0.0, 0.0, 1.0 };
			layout.rowHeights = new int[] { 1, 1, 1 };
			layout.columnWeights = new double[] { 0.0, 1.0, 0 };
			layout.columnWidths = new int[] { 1, 1, 1 };

			setLayout(layout);
			Insets insets = new Insets(5, 5, 5, 5);

			{
				text_filter = new JTextField("");
				text_filter.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						updateFilter();
					}
				});

				add(text_filter, new GridBagConstraints(0, 0, 2, 1, 0, 0,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						insets, 0, 0));
			}

			{
				button_refresh = new JButton("refresh");
				button_refresh.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						searchDevices();
					}
				});

				add(button_refresh, new GridBagConstraints(2, 0, 1, 1, 0, 0,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						insets, 0, 0));
			}

			{
				text_infos = new JTextField("");
				text_infos.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						String[] infos = text_infos.getText().split(",");

						keys = new LinkedList<Object>();
						for (String s : infos) {
							if (s == null || s.trim().isEmpty())
								continue;

							try {
								keys.add(SExpressionParser.parseUTF8(s));
							} catch (TreeParserException e1) {
								JOptionPane.showMessageDialog(DeviceList.this,
										s + " is not a valid key", "Error",
										JOptionPane.ERROR_MESSAGE);
								return;
							}
						}

						updateFilter();
						updateModel();
					}
				});

				text_infos.setToolTipText("comma-separated list of keys");

				add(new JLabel("show infos: "), new GridBagConstraints(0, 1, 1,
						1, 0, 0, GridBagConstraints.CENTER,
						GridBagConstraints.HORIZONTAL, insets, 0, 0));

				add(text_infos, new GridBagConstraints(1, 1, 2, 1, 0, 0,
						GridBagConstraints.CENTER,
						GridBagConstraints.HORIZONTAL, insets, 0, 0));
			}

			{
				table_devices = new JTable(tableModel_device);
				scroll_devices = new JScrollPane(table_devices);
				tableSorter = new TableRowSorter<DeviceTableModel>(
						tableModel_device);
				table_devices.setRowSorter(tableSorter);
				scroll_devices.setMinimumSize(new Dimension(50, 50));

				table_devices.addMouseListener(new MouseAdapter() {
					@Override
					public void mouseClicked(MouseEvent e) {
						if (e.getClickCount() != 2)
							return;

						int row = table_devices.rowAtPoint(e.getPoint());
						row = table_devices.convertRowIndexToModel(row);
						new DetailWindow(tableModel_device.getDevice(row))
								.setVisible(true);
					}
				});
				add(scroll_devices, new GridBagConstraints(0, 2, 3, 1, 0, 0,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						insets, 0, 0));
			}
		}

		private void updateFilter() {
			String pattern = text_filter.getText();
			DeviceFilter filter = new DeviceFilter(pattern, keys);
			tableSorter.setRowFilter(filter);
		}

		private void updateModel() {
			tableModel_device.setInfoKeys(keys);
		}

	}

	private class HeartbeatMonitor extends JPanel {

		private static final long serialVersionUID = 5351965391695053356L;

		private HeartbeatTableModel tablemodel_heartbeat;

		private JTable table_hosts;
		private JScrollPane scroll_hosts;

		public HeartbeatMonitor() {
			tablemodel_heartbeat = new HeartbeatTableModel();
			infrastructure.addObserver(tablemodel_heartbeat);

			initGUI();
		}

		private void initGUI() {
			GridBagLayout layout = new GridBagLayout();
			layout.rowWeights = new double[] { 1.0 };
			layout.rowHeights = new int[] { 1 };
			layout.columnWeights = new double[] { 1.0 };
			layout.columnWidths = new int[] { 1 };

			setLayout(layout);
			Insets insets = new Insets(5, 5, 5, 5);

			{
				table_hosts = new JTable(tablemodel_heartbeat);
				scroll_hosts = new JScrollPane(table_hosts);
				add(scroll_hosts, new GridBagConstraints(0, 0, 1, 1, 0, 0,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						insets, 0, 0));
			}
		}
	}

	protected DistributedInfrastructure infrastructure;

	protected DeviceTableModel tableModel_device;

	private JTabbedPane tabs;
	private DeviceList panel_devices;
	private HeartbeatMonitor panel_monitor;

	private LogListModel listModel_log;
	private JList list_log;
	private JScrollPane scroll_log;

	JSplitPane split_DeviceLog;

	protected void searchDevices() {
		tableModel_device.clear();

		PublishedDeviceDescription pattern = new PublishedDeviceDescription(
				null, null, null, null, -1, null);
		try {
			infrastructure.searchDevice(pattern);
		} catch (InfrastructureException e1) {
			showError(e1, "Infrastructure is not running");
		}
	}

	private void initGUI() {
		setLayout(new BorderLayout());

		tabs = new JTabbedPane();

		panel_devices = new DeviceList();
		tabs.addTab("Devices", panel_devices);

		panel_monitor = new HeartbeatMonitor();
		tabs.addTab("Monitor", panel_monitor);

		list_log = new JList(listModel_log);
		scroll_log = new JScrollPane(list_log);
		scroll_log.setMinimumSize(new Dimension(50, 50));
		tabs.addTab("Log", scroll_log);

		add(tabs, BorderLayout.CENTER);
	}

	protected void showError(Exception e, String message) {
		e.printStackTrace();
		JOptionPane.showMessageDialog(this, new Object[] { message,
				new JScrollPane(new JList(e.getStackTrace())) }, "Error",
				JOptionPane.ERROR_MESSAGE);
	}

	@Override
	public void notify(
			Observable<? extends de.mmis.core.base.gui.GUIComponent.GUIEvent> sender,
			de.mmis.core.base.gui.GUIComponent.GUIEvent event) {
		// ignore
	}

	@Override
	public void initialize(Class<?> clazz) {
		// create own (local) infrastructure object and ignore device
		try {
			infrastructure = DistributedInfrastructure.createInstance(null,
					true);
		} catch (InfrastructureException e) {
			showError(e, "Unable to create infrastructure");
			System.exit(1);
			return;
		}

		tableModel_device = new DeviceTableModel(new LinkedList<Object>());
		// show only the last 250 entrys
		listModel_log = new LogListModel(250);
		infrastructure.addObserver(tableModel_device);
		infrastructure.addObserver(listModel_log);

		initGUI();

		searchDevices();
	}

	@Override
	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	@Override
	public int getScrollableUnitIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		return 10;
	}

	@Override
	public int getScrollableBlockIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		return 50;
	}

	@Override
	public boolean getScrollableTracksViewportWidth() {
		// disable scrolling
		return true;
	}

	@Override
	public boolean getScrollableTracksViewportHeight() {
		// disable scrolling
		return true;
	}
}
